// BJ extend
//
library ExtBJ initializer onInit requires Table,TimerUtils,TriggerUtils
    globals
        private location tempLoc = Location(0, 0)
    endglobals

    //=====================[Widget]=====================*/
    function IsWidgetAlive takes widget wdg returns boolean
        if wdg == null then
            return false
        endif
        if GetWidgetLife(wdg) > 0.405 then
            return true
        endif
        return false
    endfunction

    function IsDestructableTree takes destructable dest returns boolean
        local boolean isInvulnerable = IsDestructableInvulnerable(dest)
        local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),XE_DUMMY_UNITID,GetWidgetX(dest),GetWidgetY(dest),0)
        local boolean result = false

        call UnitAddAbility(u, 'Ahrl')
        if isInvulnerable then
            call SetDestructableInvulnerable(dest, false)
        endif
        set result = IssueTargetOrder(u, "harvest", dest)
        call RemoveUnit(u)
        if isInvulnerable then
            call SetDestructableInvulnerable(dest, true)
        endif
        set u = null
        return result
    endfunction

    function EnumDestructablesInCircle takes real x, real y, real radius, code callBack returns nothing
        local rect r
        if radius >= 0 then
            set r = Rect(x - radius, y - radius, x + radius, y + radius)
            call EnumDestructablesInRect(r, Condition(callBack), null)
            call RemoveRect(r)
            set r = null
        endif
    endfunction


    /*=====================[Unit]=====================*/
    /**
    *
    * 设置单位可飞行
    *
    */
    function SetUnitFlyable takes unit u returns nothing
        call UnitAddAbility(u, 'Amrf')
        call UnitRemoveAbility(u, 'Amrf')
    endfunction

    /**
    *
    * 设置单位是否可移动
    *
    */
    function SetUnitMoveable takes unit u, boolean moveable returns nothing
        if moveable then
            call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u))
        else
            call SetUnitPropWindow(u, 0)
        endif
    endfunction
    /**
    *
    * 设置单位是否可攻击
    *
    */
    function SetUnitAttackable takes unit u, boolean attackable returns nothing
        if attackable then
            call UnitAddAbility(u, 'Aatk')
        else
            call UnitRemoveAbility(u, 'Aatk')
        endif
    endfunction
    /**
    *
    * 创建单位，对于玩家p使用不同的模型
    *
    */
    function CreateUnitDiffModel takes player p, integer unitId1, integer unitId2, real x, real y, real facing returns unit
        local integer id = unitId1
        if GetLocalPlayer() == p then
            set id = unitId2
        endif
        return CreateUnit(p, id, x, y, facing)
    endfunction
    /**
    *
    * 单位是否存活
    *
    */
    function IsUnitAlive takes unit u returns boolean
        return IsWidgetAlive(u)
    endfunction
    /**
    *
    * 单位是否满生命
    *
    */
    function IsUnitFullLife takes unit u returns boolean
        return not (GetUnitState(u, UNIT_STATE_LIFE) < GetUnitState(u, UNIT_STATE_MAX_LIFE))
    endfunction
    /**
    *
    * 单位是否满魔法
    *
    */
    function IsUnitFullMana takes unit u returns boolean
        return not (GetUnitState(u, UNIT_STATE_MANA) < GetUnitState(u, UNIT_STATE_MAX_MANA))
    endfunction
    /**
    *
    * 单位是否可被伤害
    *
    */
    function IsUnitGost takes unit u returns boolean
        if GetUnitAbilityLevel(u,'Aloc') > 0 then //蝗虫
            return true
        endif
        if GetUnitAbilityLevel(u,'Avul') > 0 then //隐形
            return true
        endif
        return false
    endfunction
    /**
    *
    * 选取范围内单位做动作
    *
    */
    function EnumUnitsInRange takes real x, real y, real radius, code callback returns nothing
        local boolexpr filter = Condition(callback)
        call GroupEnumUnitsInRange(ENUM_GROUP, x, y, radius, filter)
        call DestroyBoolExpr(filter)
        set filter = null
    endfunction
    /**
    *
    * 刷新单位属性面板
    *
    */
    function RefreshUnitUI takes unit u returns nothing
        call UnitAddAbility(u, 'Adis')
        call UnitRemoveAbility(u, 'Adis')
    endfunction
    /**
    *
    * 刷新单位所属颜色
    *
    */
    function RefreshUnitEffectColor takes nothing returns nothing
        call SetAllyColorFilterState(2)
        call SetAllyColorFilterState(0)
    endfunction
    /**
    *
    * 设置单位所属玩家是否联盟
    *
    */
    function SetUnitAlliance takes unit us, unit uy, boolean isAlliance returns nothing
        call SetPlayerAlliance(GetOwningPlayer(us), GetOwningPlayer(uy), ALLIANCE_PASSIVE, isAlliance)
        call SetPlayerAlliance(GetOwningPlayer(uy), GetOwningPlayer(us), ALLIANCE_PASSIVE, isAlliance)
    endfunction
    /**
    *
    * 为单位的联盟玩家共享单位的视野
    *
    */
    function UnitShareVisionForAlly takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            if IsUnitAlly(whichUnit, Player(i)) then
                call UnitShareVision(whichUnit, Player(i), share)
            endif
            set i = i + 1
        endloop
    endfunction
    /**
    *
    * 为单位的敌对玩家共享单位的视野
    *
    */
    function UnitShareVisionForEnemy takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            if IsUnitEnemy(whichUnit, Player(i)) then
                call UnitShareVision(whichUnit, Player(i), share)
            endif
            set i = i + 1
        endloop
    endfunction
    /**
    *
    * 为所有玩家共享单位的视野
    *
    */
    function UnitShareVisionForAll takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            call UnitShareVision(whichUnit, Player(i), share)
            set i = i + 1
        endloop
    endfunction

    //=====================[System]=====================
    // func : first statement -> carryDataType d = GetDelayTimerData();
    function DelayExecuteFunc takes real delay, integer carryData, code func returns nothing
        local timer tm = NewTimer()
        call SetTimerData(tm, carryData)
        call TimerStart(tm, delay, false, func)
        set tm = null
    endfunction

    //=====================[Player]=====================
    function IsPlayerPlaying takes player p returns boolean
        return GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING
    endfunction

    function IsPlayerControllerIsUser takes player p returns boolean
        return GetPlayerController(p) == MAP_CONTROL_USER
    endfunction

    function SetPlayerAllianceEx takes player playera, player playerb, boolean isAlliance returns nothing
        call SetPlayerAlliance(playera, playerb, ALLIANCE_PASSIVE, isAlliance)
        call SetPlayerAlliance(playerb, playera, ALLIANCE_PASSIVE, isAlliance)
    endfunction

    //=====================[Math]=====================
    function GetPointZ takes real x, real y returns real
        call MoveLocation(tempLoc,x,y)
        return GetLocationZ(tempLoc)
    endfunction

    function GetUnitZ takes unit u returns real
        call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
        return GetLocationZ(tempLoc) + GetUnitFlyHeight(u)
    endfunction

    function SetUnitZ takes unit u, real z returns nothing
        call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
        call SetUnitFlyHeight(u, z-GetLocationZ(tempLoc), 0.00)
    endfunction

    function SetUnitXY takes unit u, real x, real y returns nothing
        call SetUnitX(u, x)
        call SetUnitY(u, y)
    endfunction

    function SetUnitXYZ takes unit u, real x, real y, real z returns nothing
        call SetUnitX(u, x)
        call SetUnitY(u, y)
        call SetUnitZ(u, z)
    endfunction

    function GetDistanceX takes real dis, real angle returns real
        return dis*Cos(angle * bj_DEGTORAD)
    endfunction

    function GetDistanceY takes real dis, real angle returns real
        return dis*Sin(angle * bj_DEGTORAD)
    endfunction

    function GetAngleByXY takes real x1,real y1,real x2,real y2 returns real
        return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
    endfunction

    function GetAngleByUnitXY takes unit u1, unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        return GetAngleByXY(x1, y1, x2, y2)
    endfunction

    function GetDistanceByXY takes real x1,real y1,real x2,real y2 returns real
        set x1 = x2 - x1
        set y1 = y2 - y1
        return SquareRoot(x1*x1 + y1*y1)
    endfunction

    function GetDistanceByUnitXY takes unit u1, unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        return GetDistanceByXY(x1, y1, x2, y2)
    endfunction

    function GetDistanceByUnitXYZ takes unit u1,unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real z1 = GetUnitZ(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        local real z2 = GetUnitZ(u2)
        local real dx = x2 - x1
        local real dy = y2 - y1
        local real dz = z2 - z1
        return SquareRoot(dx * dx + dy * dy + dz * dz)
    endfunction

    function Round takes real r returns real
        return I2R(R2I(r+0.5))
    endfunction

    ///*=====================[Integer]=====================*/
    function IntegerLength takes integer i returns integer
        local integer len = 0
        if i==0 then
            return 1
        endif

        set len = 0
        loop
            exitwhen i == 0
            set i = i / 10
            set len = len + 1
        endloop

        return len
    endfunction

    ///*=====================[Effect]=====================*/
    function AddEffectTarget takes string modelName, widget targetWidget, string attachPointName returns nothing
        call DestroyEffect(AddSpecialEffectTarget(modelName, targetWidget, attachPointName))
    endfunction

    function AddEffect takes string modelName, real x, real y returns nothing
        call DestroyEffect(AddSpecialEffect(modelName, x, y))
    endfunction

    ///*=====================[Camera]=====================*/
    //SetCameraField() has an input of degrees, but GetCameraField() returns radians.
    //This script will keep everything in degrees for ease of usage.
    function GetCameraFieldEx takes camerafield whichField returns real
        if whichField != CAMERA_FIELD_FARZ and whichField != CAMERA_FIELD_TARGET_DISTANCE and whichField != CAMERA_FIELD_ZOFFSET then
            return GetCameraField(whichField) * bj_RADTODEG
        endif
        return GetCameraField(whichField)
    endfunction
    //This function will control whether or not transmissions can be skipped
    function EnableTransmissionSkip takes boolean flag returns nothing
        if flag then
            call DestroyTrigger(bj_cineSceneBeingSkipped) //just in case it was created in this function
            call TryInitCinematicBehaviorBJ() //recreate it
        else
            if bj_cineSceneBeingSkipped == null then //if it is null, create it
                set bj_cineSceneBeingSkipped = CreateTrigger() //creating it without events will bypass the init behavior
            else
                call DisableTrigger(bj_cineSceneBeingSkipped) //disable it if it already exists
            endif
        endif
    endfunction

    ///*=====================[Ability]=====================*/
    function UnitAddAbilityPermanent takes unit u, integer abilityId returns nothing
        call UnitAddAbility(u,abilityId)
        call UnitMakeAbilityPermanent(u,true,abilityId)
    endfunction

    private function onInit takes nothing returns nothing
    endfunction

endlibrary
